package cn.txun05.csmall.product.filter;


import cn.txun05.csmall.commons.security.LoginPrincipal;
import cn.txun05.csmall.commons.web.JsonResult;
import cn.txun05.csmall.commons.web.ServiceCode;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;

/**
 * JWT过滤器：接受jwt、解析JWT，
 * 将解析得到的数据封装为认证信息并存入SecurituyContext中
 */
@Slf4j
@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {
    @Value("${csmall.jwt.secretKey}")
    private String secretKey;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        log.debug("JwtAuthorizationFilter开始执行过滤....");
        //根据业内惯用的做法，客户端将jwt放在请求头(Request Header)中的Authorization属性中
        String jwt = request.getHeader("Authorization");
        log.debug("客户端携带的jwt：{}",jwt);
        //判断客户端是否携带了有效的jwt
        if(!StringUtils.hasText(jwt)){
            //如果jwt无效，则放行,由Spring Security的过滤器进行拦截
            chain.doFilter(request,response);
            return;
        }
        //尝试解析JWT
        //String secretKey="q23adcw4ere56te45ye4gty54..wew";
        Claims claims = null;

        response.setContentType("text/html;charset=utf-8");
        try {
            claims = Jwts.parser().setSigningKey(secretKey)
                    .parseClaimsJws(jwt).getBody();
        }catch (SignatureException e){
            //日志
            String message = "非法访问！";
            log.warn("程序运行过程中出现了SignatureException，将向客户端响应错误信息");
            log.warn("错误信息:{}",message);
            //JsonResult对象转json
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_SIGNATURE,message);
            String jsonString = JSON.toJSONString(jsonResult);
            //响应
            //response.setContentType("text/html;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.println(jsonString);
            writer.close();
            return;
        }catch (MalformedJwtException e){
            //日志
            String message = "非法访问！";
            log.warn("程序运行过程中出现了MalformedJwtException，将向客户端响应错误信息");
            log.warn("错误信息:{}",message);
            //JsonResult对象转json
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_MALFORMED,message);
            String jsonString = JSON.toJSONString(jsonResult);
            //响应
            //response.setContentType("text/html;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.println(jsonString);
            writer.close();
            return;
        }catch (ExpiredJwtException e){
            //日志
            String message = "您的登录信息已经过期，请重新登录！";
            log.warn("程序运行过程中出现了ExpiredJwtException，将向客户端响应错误信息");
            log.warn("错误信息:{}",message);
            //JsonResult对象转json
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_EXPIRED,message);
            String jsonString = JSON.toJSONString(jsonResult);
            //响应
            //response.setContentType("text/html;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.println(jsonString);
            writer.close();
            return;
        }catch (Throwable e){
             //日志
            String message = "服务器忙，请稍后再试！【在开发过程中，如果看到此提示，应该检查服务器端的控制台，分析异常，并在解析JWT的过滤器中补充处理对应异常的代码块】";
            log.warn("程序运行过程中出现了Throwable，将向客户端响应错误信息！");
            log.warn("异常：", e);
            log.warn("错误信息:{}",message);
            //JsonResult对象转json
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_UNKNOWN,message);
            String jsonString = JSON.toJSONString(jsonResult);
            //响应
            //response.setContentType("text/html;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.println(jsonString);
            writer.close();
            return;
        }

        Long id = claims.get("id",Long.class);
        String username = claims.get("username",String.class);
        String authoritiesJsonString = claims.get("authoritiesJsonString",String.class);
        //System.out.println("id="+id);
        //System.out.println("username="+username);
         //需要考虑使用什么类型数据作为当事人，使用自定义类，根据需求自行添加属性
        //TODO 如何添加真实的权限
        Object principal = new LoginPrincipal().setId(id).setUsername(username);//可以是任何类型。暂时使用用户名
        Object credentials = null;//本次不需要使用密码
        Collection<SimpleGrantedAuthority> authorities =
                //解析权限JSON字符串，将每个权限封装成SimpleGrantedAuthority对象
                //进一步封装成权限集合
                JSON.parseArray(authoritiesJsonString,SimpleGrantedAuthority.class);
        //创建认证信息
        Authentication authentication
                = new UsernamePasswordAuthenticationToken(principal,credentials,authorities);

        //获取SecurityContext上下文对象，将认证结果存入SecurityContext中
        SecurityContext securityContext = SecurityContextHolder.getContext();
        securityContext.setAuthentication(authentication);

        chain.doFilter(request,response);
    }
}
